Gaming Data Overview and Backgrond Knowledge
1.Brief description and data soure
The gaming data set has information of video games with sales greater than 100,000 copies since the year 1980 to 2016. This gaming data set is downloaded from Kaggle.com.
Mobile, PC/Mac(computer), social/online, and console are common gaming platforms. Console here refers to a computer device that outputs a video signal or visual image to display a video game that one or more people can play. Some popular consoles are PlayStation 4 Pro, Xbox One X, Nintendo Switch. Our dataset is about videogames played on console only.
2.Data Cleaning
- We \(\color{red}{\text{removed}}\) all NULL values, Unknown values, and data in 2017 or later.
- We \(\color{red}{\text{converted}}\) Year from String to Numeric.
- We \(\color{red}{\text{added}}\) a new column called Portable based on if the gaming console is portable or not.
- We have 16187 rows and 12 variables after cleaning the data and adding the Portable variable.
options(stringsAsFactors = FALSE)
Portable <- function(df) {
len <- length(df$Platform)
new_vec <- vector(mode = "numeric", length = len)
protvec <- c("DS", "GB", "3DS", "GBA")
for (i in 1:len) {
if (df$Platform[i] %in% protvec) {
new_vec[i] <- 1
} else {
new_vec[i] <- 0
}
}
return(new_vec)
}
3.Variables
| Rank |
Ranking of overall sales |
| Name |
The games name |
| Platform |
Platform of the games release (i.e. PC,PS4, etc.) |
| Year |
Year of the game’s release |
| Genre |
Genre - Genre of the game |
| Publisher |
Publisher of the game |
| NA_Sales |
Sales in North America (in millions) |
| EU_Sales |
Sales in Europe (in millions) |
| JP_Sales |
Sales in Japan (in millions) |
| Other_Sales |
Sales in the rest of the world (in millions) |
| Global_Sales |
Total worldwide sales |
| Portable |
If the gaming console is portable (1=yes,0=no) |
4.Glimpse of Data
a.
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 16187 obs. of 12 variables:
$ Rank : num 3825 1679 1879 1711 638 ...
$ Name : chr "Seaman" "NFL 2K" "NFL 2K1" "Shenmue" ...
$ Platform : chr "DC" "DC" "DC" "DC" ...
$ Year : num 1999 1999 2000 1999 1998 ...
$ Genre : chr "Simulation" "Sports" "Sports" "Adventure" ...
$ Publisher : chr "Sega" "Sega" "Sega" "Sega" ...
$ NA_Sales : num 0 1.12 1.02 0.52 1.26 1.1 0.41 0 0 0 ...
$ EU_Sales : num 0 0.05 0.05 0.24 0.61 0.51 0.23 0 0 0 ...
$ JP_Sales : num 0.52 0 0 0.38 0.46 0.12 0.47 1.01 0.54 0.62 ...
$ Other_Sales : num 0 0.02 0.02 0.04 0.08 0.08 0.03 0 0 0 ...
$ GLobal_Sales: num 0.52 1.2 1.09 1.18 2.42 1.81 1.14 1.01 0.54 0.62 ...
$ Portable : num 0 0 0 0 0 0 0 1 1 1 ...
c. 12 unique genres:
Simulation, Sports, Adventure, Platform, Racing, Action, Misc, Role-Playing, Puzzle, Fighting, Strategy, Shooter
d. 575 unique publishers:
Top 10 Publishers based on frequency: Electronic Arts, Activision, Namco Bandai Games, Ubisoft, Konami Digital Entertainment, THQ, Nintendo, Sony Computer Entertainment, Sega, Take-Two Interactive
1.Global trends and analysis
1.b Top 10 best selling publishers over time?
- Top 3 publishers are Nintendo, EA, and Activision.
top10 <- games %>%select(Publisher,GLobal_Sales)%>%group_by(Publisher)%>%summarise(GLobal_Sales=sum(GLobal_Sales))%>%arrange(desc(GLobal_Sales))%>%head(10)
p <- ggplot(top10, aes(x=reorder(Publisher,-GLobal_Sales),y=GLobal_Sales,,label=round(GLobal_Sales,2))) +
stat_summary(fun.y=sum, geom="bar",position=position_dodge(1),width=0.8,show.legend = F,,col="black",fill="skyblue")+
labs(title = "Top 10 best selling publishers",caption = "Sales in Million")+
geom_text(col="black",size=4,vjust=-1)+
ylab("Global Sales")+xlab("Publisher")+
scale_x_discrete(labels = function(x) str_wrap(x, width =1))
p

1.d Best selling genre for the TOP 3 publishers
- Top 3 genres for each publisher:
- Nintendo: Sports, Role-playing, Platform
- EA: sports, shooter, racing
- Activision: sports, shooter, action
top_three_publisher <- subset(games,Publisher %in% c("Nintendo","Electronic Arts","Activision"))
top_three_publisher$Portable <- factor(top_three_publisher$Portable)
top_three_publisher_platform <- top_three_publisher %>%
select(Publisher,Genre,Portable,GLobal_Sales)%>%
group_by(Publisher,Genre,Portable)%>%
summarise(GLobal_Sales=sum(GLobal_Sales))%>%
arrange(desc(GLobal_Sales))
top_new <- top_three_publisher_platform%>%group_by(Publisher)%>%top_n(3)
top_new$Publisher_g = factor(top_new$Publisher, levels=c("Nintendo","Electronic Arts","Activision"))
ggplot(top_new, aes(x=Genre, y=GLobal_Sales, color=Publisher,shape=Portable)) +
geom_point(size=3)+
scale_color_discrete(breaks=c("Nintendo","Electronic Arts","Activision"))+
labs(title="The top 3 genre for the top 3 publishers",caption="Sales in Million") +
geom_segment(aes(x=Genre,xend=Genre, y=0, yend=GLobal_Sales))+
geom_text(aes(label=GLobal_Sales), hjust = -0.3, size = 2.6,fontface = "bold",color='black') +
theme( plot.title = element_text(size=17,hjust=-0.5)) +
facet_wrap(~ Publisher_g, nrow = 5, scales = 'free', strip.position = 'right')+
ylim(0, max(top_new$GLobal_Sales + 10))+ylab("Global Sales")+
coord_flip()

2. Regional trends and analysis
2.a Top 3 best selling platform by regions
- We studied the regional trends by looking at the top platforms for each region as shown from the bar chart.
games <- games[!(games$Year %in% c("N/A", "2017", "2020")),]
games <- games %>% gather(Region, Revenue, 7:10)
games$Region <- factor(games$Region)
mytheme_1 <- function() {
return(theme(axis.text.x = element_text(angle = 90, size = 10, vjust = 0.4), plot.title = element_text(size = 15, vjust = 2),axis.title.x = element_text(size = 12, vjust = -0.35)))
}
mytheme_2 <- function() {
return(theme(axis.text.x = element_text(size = 10, vjust = 0.4), plot.title = element_text(size = 15, vjust = 2),axis.title.x = element_text(size = 12, vjust = -0.35)))
}
mycolors <- c("#771C19", "#AA3929", "#8E9CA3", "#556670", "#000000", "#E25033", "#F27314", "#F8A31B", "#E2C59F", "#B6C5CC","#771C19", "#AA3929", "#8E9CA3", "#556670", "#000000", "#E25033", "#F27314", "#F8A31B", "#E2C59F", "#B6C5CC","#771C19", "#AA3929", "#8E9CA3", "#556670", "#000000", "#E25033", "#F27314", "#F8A31B", "#E2C59F", "#B6C5CC","#8E9CA3")
top_platform_region <- games %>%
group_by(Region, Platform) %>%
summarize(Revenue = sum(Revenue)) %>%
arrange(desc(Revenue)) %>%
top_n(3)
Selecting by Revenue
ted <- ggplot(top_platform_region, aes(Region, Revenue, fill = Platform)) +
geom_bar(position = "dodge", stat = "identity") +
ggtitle("Top 3 best selling platform by regions") +
ylab("Revenue in Millions") +
xlab("Region") +
mytheme_2() +
theme(legend.position = "top") +
scale_fill_manual(values = c("#8E9CA3","#F8A31B", "#AA3929", "#E25033", "#E2C59F", "#556670"))
ggplotly(ted)
2.b Top 3 best selling publisher by regions
- We studied the regional trends by looking at the top 3 best selling publishers for each region.
top_genres_region <- games %>%
group_by(Region, Publisher) %>%
summarize(Revenue = sum(Revenue)) %>%
arrange(desc(Revenue)) %>%
top_n(3)
ted2 <- ggplot(top_genres_region, aes(Region, Revenue, fill = Publisher)) +
geom_bar(position = "dodge", stat = "identity") +
ggtitle("Top 3 best selling publisher by region") +
ylab("Sales in Millions") +
xlab("Region") +
mytheme_2() +
theme(legend.position = "top")
ggplotly(ted2)
2.c Best selling genre for particular regions
*This heat map identfies the top selling genres for each region by displaying a deeper color (purple) for genres with high revenues.
year_genre <- games %>%
group_by(Year, Genre, Region) %>%
summarise(TotalRevenue = sum(Revenue))
ggplot(year_genre, aes(Year, Genre, fill = TotalRevenue)) +
geom_tile(color = "white") +
ggtitle(" Best selling genre for particular regions") +
facet_wrap(vars(Region), ncol = 4) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
scale_color_gradient(low="pink", high= "purple")+
scale_fill_gradient(low="pink", high= "purple")

2.d Top 3 best selling genre by region
- We studied the regional trends by looking at the top 3 best selling genres for each region.
top_genres_region <- games %>%
group_by(Region, Genre) %>%
summarize(Revenue = sum(Revenue)) %>%
arrange(desc(Revenue)) %>%
top_n(3)
ted2 <- ggplot(top_genres_region, aes(Region, Revenue, fill = Genre)) +
geom_bar(position = "dodge", stat = "identity") +
ggtitle("Top 3 best selling genre by region") +
ylab("Revenue in Millions") +
xlab("Region") +
mytheme_2() +
theme(legend.position = "top")
ggplotly(ted2)
3 Investment options and recommendations
3.a Global sales proportion by region
- For the most part we see that the North America accounts for the highest proportion of global sales. We can also see that European sales are on an incline and actually surpass North american sales by the year 2015-2016. Even though Japan’s proportion of Global sales seem to be declining in the past years, for the most recent years it seems to be steadily inclining.
games <- read_csv("games.csv")
df_trial <- data_frame(sort(games$Year), NA_Sales = games$NA_Sales, games$EU_Sales,
games$JP_Sales, games$Other_Sales, games$GLobal_Sales)
games3 <- games %>%
select(Year, NA_Sales, EU_Sales, JP_Sales, Other_Sales,GLobal_Sales) %>%
group_by(Year) %>%
summarise(NA_sales_prop = sum(NA_Sales)/sum(GLobal_Sales),
EU_sales_prop = sum(EU_Sales)/sum(GLobal_Sales),
JP_sales_prop = sum(JP_Sales)/sum(GLobal_Sales),
Other_sales_prop = sum(Other_Sales)/sum(GLobal_Sales),
Global_sales_prop = sum(GLobal_Sales)/sum(GLobal_Sales))
mycolors <- c("#771C19", "#AA3929", "#8E9CA3", "#556670", "#000000", "#E25033", "#F27314", "#F8A31B", "#E2C59F", "#B6C5CC")
regions <- c( "darkgreen" = "North America", "blue" = "Europe" , "sienna" = "Japan" , "orange" = "Other Regions", "black" = "Global")
ggplot(games3,aes(x=Year))+
geom_line(aes(y = NA_sales_prop ,color = "#771C19"))+
geom_line(aes(y = EU_sales_prop , color = "#B6C5CC"))+
geom_line(aes(y = JP_sales_prop, color = "#E25033" ))+
geom_line(aes(y = Other_sales_prop, color = "#E2C59F"))+
geom_line(aes(y = Global_sales_prop, color = "black"))+
labs(title = "Sales per region from 1980-2016", y = "Percentage of global sales")+
scale_color_manual(name = "Regions", values = c( "darkgreen", "blue", "sienna" , "orange", "black"),labels=c("North America", "Europe" , "Japan" , "Other Regions", "Global"))

3.b Summary
| Publisher |
Nintendo |
Nintendo |
Nintendo |
| Platform |
X360 |
PS3 |
DS |
| Genre |
Action |
Action |
Role-Playing |
Appendix
Data Exploration.
ggplot(games, aes(x=Year, fill=..count..)) +
geom_bar()+
scale_color_gradient(low="#771C19", high= "#F27314")+
scale_fill_gradient(low="#771C19", high= "#F27314")+
labs(title="Number of Games Released every Year", x= "Year",
y= "Total Number of Games")+
geom_text(stat='count',aes(label=..count..), hjust=-0.1,color="black", size=2.5)+
scale_x_continuous(breaks = 1980:2016) + theme_minimal()+
coord_flip()

Data Exploration 2

Data Exploration 3
ok <- games %>% select(Year,GLobal_Sales,Genre)%>%group_by(Year,Genre)%>%
summarise(Total_sales=sum(GLobal_Sales))
ok1 <- arrange(ok, desc(Year))
plot_ly(ok1, x = ~Total_sales, y = ~Genre, z = ~Year) %>% layout( title ="Sales by genre from 1980 - 2016") %>%
add_markers(color = ~Genre, size = 0.5)
LS0tCnRpdGxlOiAiVGVhbSA1IEdhbWluZyBEYXRhIE5vdGVib29rIgpzdWJ0aXRsZTogIiFbXShnYW1lMi5wbmcpe3dpZHRoPTI1MH0iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KKipUZWFtIE1lbWJlcnM6IEZ1Y2hlbmcgWWFvLCBIdWFpcGluZyBXYW5nLCBMaW1laSBIdWFuZywgRW1hbiBOYWdpYiwgS3dhbmd3b28gS2ltKiogCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQojIGxvYWRpbmcgbGlicmFyaWVzCmxpYnJhcnkoRFQpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShwbG90bHkpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkod2VzYW5kZXJzb24pCmdhbWVzIDwtIHJlYWRfY3N2KCJnYW1lcy5jc3YiKSAKbXljb2xvcnMgPC0gYygiIzc3MUMxOSIsICIjQUEzOTI5IiwgIiM4RTlDQTMiLCAiIzU1NjY3MCIsICIjMDAwMDAwIiwgIiNFMjUwMzMiLCAiI0YyNzMxNCIsICIjRjhBMzFCIiwgIiNFMkM1OUYiLCAiI0I2QzVDQyIpCgojIG1vZGlmeWluZyBjaGFydCBzaXplCm9wdGlvbnMocmVwci5wbG90LndpZHRoPTI1MCwgcmVwci5wbG90LmhlaWdodD0xMDApCmBgYAoKCiMjIyBHYW1pbmcgRGF0YSBPdmVydmlldyBhbmQgQmFja2dyb25kIEtub3dsZWRnZQojIyMjIyAxLkJyaWVmIGRlc2NyaXB0aW9uIGFuZCBkYXRhIHNvdXJlClRoZSBnYW1pbmcgZGF0YSBzZXQgaGFzIGluZm9ybWF0aW9uIG9mIHZpZGVvIGdhbWVzIHdpdGggc2FsZXMgZ3JlYXRlciB0aGFuIDEwMCwwMDAgY29waWVzIHNpbmNlIHRoZSB5ZWFyIDE5ODAgdG8gMjAxNi4gVGhpcyBbZ2FtaW5nIGRhdGEgc2V0XShodHRwczovL3d3dy5rYWdnbGUuY29tL2dyZWdvcnV0L3ZpZGVvZ2FtZXNhbGVzKSBpcyBkb3dubG9hZGVkIGZyb20gW0thZ2dsZS5jb21dKHd3dy5rYWdnbGUuY29tKS4KCk1vYmlsZSwgUEMvTWFjKGNvbXB1dGVyKSwgc29jaWFsL29ubGluZSwgYW5kIGNvbnNvbGUgYXJlIGNvbW1vbiBnYW1pbmcgcGxhdGZvcm1zLiBDb25zb2xlIGhlcmUgcmVmZXJzIHRvIGEgY29tcHV0ZXIgZGV2aWNlIHRoYXQgb3V0cHV0cyBhIHZpZGVvIHNpZ25hbCBvciB2aXN1YWwgaW1hZ2UgdG8gZGlzcGxheSBhIHZpZGVvIGdhbWUgdGhhdCBvbmUgb3IgbW9yZSBwZW9wbGUgY2FuIHBsYXkuIFNvbWUgcG9wdWxhciBjb25zb2xlcyBhcmUgUGxheVN0YXRpb24gNCBQcm8sIFhib3ggT25lIFgsIE5pbnRlbmRvIFN3aXRjaC4gT3VyIGRhdGFzZXQgaXMgYWJvdXQgdmlkZW9nYW1lcyBwbGF5ZWQgb24gY29uc29sZSBvbmx5LiAgCgotLS0KCiMjIyMjIDIuRGF0YSBDbGVhbmluZyAKKiBXZSAkXGNvbG9ye3JlZH17XHRleHR7cmVtb3ZlZH19JCBhbGwgKipOVUxMKiogdmFsdWVzLCAqKlVua25vd24qKiB2YWx1ZXMsIGFuZCBkYXRhIGluICoqMjAxNyBvciBsYXRlcioqLgoqIFdlICRcY29sb3J7cmVkfXtcdGV4dHtjb252ZXJ0ZWR9fSQgWWVhciBmcm9tICoqU3RyaW5nKiogdG8gKipOdW1lcmljKiouIAoqIFdlICRcY29sb3J7cmVkfXtcdGV4dHthZGRlZH19JCBhIG5ldyBjb2x1bW4gY2FsbGVkICpQb3J0YWJsZSogYmFzZWQgb24gaWYgdGhlIGdhbWluZyBjb25zb2xlIGlzIHBvcnRhYmxlIG9yIG5vdC4KKiBXZSBoYXZlIGByIG5yb3coZ2FtZXMpYCByb3dzIGFuZCBgciBuY29sKGdhbWVzKWAgdmFyaWFibGVzIGFmdGVyIGNsZWFuaW5nIHRoZSBkYXRhIGFuZCBhZGRpbmcgdGhlICpQb3J0YWJsZSogdmFyaWFibGUuIAoKYGBge3IgZWNobz1UUlVFfQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKUG9ydGFibGUgPC0gZnVuY3Rpb24oZGYpIHsKIGxlbiA8LSBsZW5ndGgoZGYkUGxhdGZvcm0pCiBuZXdfdmVjIDwtIHZlY3Rvcihtb2RlID0gIm51bWVyaWMiLCBsZW5ndGggPSBsZW4pCiBwcm90dmVjIDwtIGMoIkRTIiwgIkdCIiwgIjNEUyIsICJHQkEiKQogZm9yIChpIGluIDE6bGVuKSAgewogICBpZiAoZGYkUGxhdGZvcm1baV0gJWluJSBwcm90dmVjKSB7CiAgICAgbmV3X3ZlY1tpXSA8LSAxCiAgIH0gZWxzZSB7CiAgICAgbmV3X3ZlY1tpXSA8LSAwCiAgIH0KIH0KIHJldHVybihuZXdfdmVjKQp9CgpgYGAKCmBgYHtyfQpnYW1lcyRQb3J0YWJsZSA8LSBQb3J0YWJsZShnYW1lcykKYGBgCgotLS0KCiMjIyMjIDMuVmFyaWFibGVzIAp8IFZhcmlhYmxlICAgfCAgICAgIERlc2NyaXB0aW9uICAgICAgfAp8LS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLTp8CnwgUmFuayB8ICBSYW5raW5nIG9mIG92ZXJhbGwgc2FsZXMgfCAKfCBOYW1lIHwgICAgVGhlIGdhbWVzIG5hbWUgICB8ICAgCnwgUGxhdGZvcm0gfCBQbGF0Zm9ybSBvZiB0aGUgZ2FtZXMgcmVsZWFzZSAoaS5lLiBQQyxQUzQsIGV0Yy4pIHwgICAgCnwgWWVhciB8IFllYXIgb2YgdGhlIGdhbWUncyByZWxlYXNlIHwKfCBHZW5yZSB8IEdlbnJlIC0gR2VucmUgb2YgdGhlIGdhbWUgfAp8IFB1Ymxpc2hlciB8UHVibGlzaGVyIG9mIHRoZSBnYW1lIHwKfE5BX1NhbGVzfFNhbGVzIGluIE5vcnRoIEFtZXJpY2EgKGluIG1pbGxpb25zKSB8IAp8IEVVX1NhbGVzfCBTYWxlcyBpbiBFdXJvcGUgKGluIG1pbGxpb25zKSB8CnxKUF9TYWxlcyB8U2FsZXMgaW4gSmFwYW4gKGluIG1pbGxpb25zKSB8CnwgT3RoZXJfU2FsZXN8U2FsZXMgaW4gdGhlIHJlc3Qgb2YgdGhlIHdvcmxkIChpbiBtaWxsaW9ucykgfAp8R2xvYmFsX1NhbGVzfFRvdGFsIHdvcmxkd2lkZSBzYWxlcyB8CnwgUG9ydGFibGUgfCBJZiB0aGUgZ2FtaW5nIGNvbnNvbGUgaXMgcG9ydGFibGUgKDE9eWVzLDA9bm8pIHwKCi0tLQoKIyMjIyMgNC5HbGltcHNlIG9mIERhdGEgCiMjIyMjIyBhLgpgYGB7cn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBGQUxTRQopCnN0cihnYW1lcyxnaXZlLmF0dHI9RikKYGBgCgoKIyMjIyMjIGIuICoqYHIgbGVuZ3RoKHVuaXF1ZShnYW1lcyRQbGF0Zm9ybSkpYCB1bmlxdWUgcGxhdGZvcm1zOioqICAgIApgciB1bmlxdWUoZ2FtZXMkUGxhdGZvcm0pIGAKCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnVuaXF1ZShnYW1lcyRQbGF0Zm9ybSkKYGBgCgoKIyMjIyMjIGMuICoqYHIgbGVuZ3RoKHVuaXF1ZShnYW1lcyRHZW5yZSkpYCB1bmlxdWUgZ2VucmVzOioqICAgCmByIHVuaXF1ZShnYW1lcyRHZW5yZSkgYAoKCiMjIyMjIyBkLiAqKmByIGxlbmd0aCh1bmlxdWUoZ2FtZXMkUHVibGlzaGVyKSlgIHVuaXF1ZSBwdWJsaXNoZXJzOioqIApUb3AgMTAgUHVibGlzaGVycyBiYXNlZCBvbiBmcmVxdWVuY3k6IGByIHRvcF90ZW4oZ2FtZXMpYAoKYGBge3IgaW5jbHVkZT1GQUxTRX0KdG9wX3RlbiA8LSBmdW5jdGlvbih4KSB7CiAgZGZfdGVtcCA8LSBjb3VudCh4LCBQdWJsaXNoZXIsIHNvcnQgPSBUUlVFKQogIHJldHVybihkZl90ZW1wJFB1Ymxpc2hlclsxOjEwXSkKfQpgYGAKCgotLS0KCiMjIyBQcm9ibGVtIFNldDogIFdoZXJlIGFuZCBob3cgdG8gaW52ZXN0IGluIHRoZSBnYW1pbmcgaW5kdXN0cnkgCiogIEdsb2JhbCB0cmVuZHMgYW5kIGFubHlzaXMgCiogIFJlZ2lvbmFsIHRyZW5kcyBhbmQgYW5hbHlzaXMgCiogIEludmVzdG1lbnQgb3B0aW9ucyBhbmQgcmVjb21tZW5kYXRpb25zIAoKCi0tLQojIyMgMS5HbG9iYWwgdHJlbmRzIGFuZCBhbmFseXNpcyAKIyMjIyMjICAxLmEgIFdoYXQgaXMgdGhlIHRvcCBwbGF0Zm9ybSBlYWNoIHllYXI/IAoqIE9uY2UgYSBwbGF0Zm9ybSBjbGlja3MgaW4gdGhlIG1hcmtldCwgaXQgZ29lcyBvbiB0byBydWxlIGZvciBhIGZldyB5ZWFycy4KKiBUaGUgUGxheXN0YXRpb24gcGxhdGZvcm0gd2FzIHBvcHVsYXIgZm9yIG5lYXJseSAyMCB5ZWFycy4KCmBgYHtyIGVjaG89VFJVRSxyZXN1bHRzPUZBTFNFfQoKdG9wX3BsYXRmb3JtcyA8LSBnYW1lcyAlPiUKICAgICAgICAgICAgIGdyb3VwX2J5KFllYXIsIFBsYXRmb3JtKSAlPiUKICAgICAgICAgICAgIHN1bW1hcml6ZShSZXZlbnVlID0gc3VtKEdMb2JhbF9TYWxlcykpICU+JQogICAgICAgICAgICAgYXJyYW5nZShkZXNjKFJldmVudWUpKSAlPiUKICAgICAgICAgICAgIHRvcF9uKDEpCmdncGxvdCh0b3BfcGxhdGZvcm1zLCBhZXMoWWVhciwgUmV2ZW51ZSwgZmlsbCA9IFBsYXRmb3JtKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdndGl0bGUoIlRvcCBQbGF0Zm9ybSBieSBSZXZlbnVlIGVhY2ggeWVhciIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteWNvbG9ycykrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShtaW4odG9wX3BsYXRmb3JtcyRZZWFyKSxtYXgodG9wX3BsYXRmb3JtcyRZZWFyKSw1KSkrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKCiMjIyMjIyAgMS5iICBUb3AgMTAgYmVzdCBzZWxsaW5nIHB1Ymxpc2hlcnMgb3ZlciB0aW1lPyAKKiBUb3AgMyBwdWJsaXNoZXJzIGFyZSBOaW50ZW5kbywgRUEsIGFuZCBBY3RpdmlzaW9uLgoKYGBge3IgZWNobz1UUlVFLGZpZy53aWR0aD0xMn0KCnRvcDEwIDwtIGdhbWVzICU+JXNlbGVjdChQdWJsaXNoZXIsR0xvYmFsX1NhbGVzKSU+JWdyb3VwX2J5KFB1Ymxpc2hlciklPiVzdW1tYXJpc2UoR0xvYmFsX1NhbGVzPXN1bShHTG9iYWxfU2FsZXMpKSU+JWFycmFuZ2UoZGVzYyhHTG9iYWxfU2FsZXMpKSU+JWhlYWQoMTApCgoKcCA8LSBnZ3Bsb3QodG9wMTAsIGFlcyh4PXJlb3JkZXIoUHVibGlzaGVyLC1HTG9iYWxfU2FsZXMpLHk9R0xvYmFsX1NhbGVzLCxsYWJlbD1yb3VuZChHTG9iYWxfU2FsZXMsMikpKSArCiBzdGF0X3N1bW1hcnkoZnVuLnk9c3VtLCBnZW9tPSJiYXIiLHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDEpLHdpZHRoPTAuOCxzaG93LmxlZ2VuZCA9IEYsLGNvbD0iYmxhY2siLGZpbGw9InNreWJsdWUiKSsKbGFicyh0aXRsZSA9ICJUb3AgMTAgYmVzdCBzZWxsaW5nIHB1Ymxpc2hlcnMiLGNhcHRpb24gPSAiU2FsZXMgaW4gTWlsbGlvbiIpKwogICBnZW9tX3RleHQoY29sPSJibGFjayIsc2l6ZT00LHZqdXN0PS0xKSsKICB5bGFiKCJHbG9iYWwgU2FsZXMiKSt4bGFiKCJQdWJsaXNoZXIiKSsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0xKSkKCnAKYGBgCgojIyMjIyMgMS5jICBCZXN0IHNlbGxpbmcgcGxhdGZvcm1zIGZvciB0aGUgVE9QIDMgcHVibGlzaGVycwoqIFRvcCAzIHBsYXRmb3JtcyBmb3IgZWFjaCBwdWJsaXNoZXI6CiogTmludGVuZG86IFdpaSwgR0IsIERTCiogRUE6IFgzNjAsIFBTMywgUFMyCiogQWN0aXZpc2lvbjogWDM2MCwgUFMzLCBQUzIKCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz1GQUxTRSxmaWcud2lkdGg9N30KdG9wX3RocmVlX3B1Ymxpc2hlciA8LSBzdWJzZXQoZ2FtZXMsUHVibGlzaGVyICVpbiUgYygiTmludGVuZG8iLCJFbGVjdHJvbmljIEFydHMiLCJBY3RpdmlzaW9uIikpCnRvcF90aHJlZV9wdWJsaXNoZXIkUG9ydGFibGUgPC0gZmFjdG9yKHRvcF90aHJlZV9wdWJsaXNoZXIkUG9ydGFibGUpCnRvcF90aHJlZV9wdWJsaXNoZXJfcGxhdGZvcm0gPC0gdG9wX3RocmVlX3B1Ymxpc2hlciAlPiVzZWxlY3QoUHVibGlzaGVyLFBsYXRmb3JtLFBvcnRhYmxlLEdMb2JhbF9TYWxlcyklPiVncm91cF9ieShQdWJsaXNoZXIsUGxhdGZvcm0sUG9ydGFibGUpJT4lc3VtbWFyaXNlKEdMb2JhbF9TYWxlcz1zdW0oR0xvYmFsX1NhbGVzKSklPiVhcnJhbmdlKGRlc2MoUHVibGlzaGVyKSkKdG9wX25ldyA8LSB0b3BfdGhyZWVfcHVibGlzaGVyX3BsYXRmb3JtJT4lZ3JvdXBfYnkoUHVibGlzaGVyKSU+JXRvcF9uKDMpCnRvcF9uZXckUHVibGlzaGVyX2YgPSBmYWN0b3IodG9wX25ldyRQdWJsaXNoZXIsIGxldmVscz1jKCJOaW50ZW5kbyIsIkVsZWN0cm9uaWMgQXJ0cyIsIkFjdGl2aXNpb24iKSkKZ2dwbG90KHRvcF9uZXcsIGFlcyh4PVBsYXRmb3JtLCB5PUdMb2JhbF9TYWxlcywgY29sb3I9UHVibGlzaGVyLHNoYXBlPVBvcnRhYmxlKSkgKwogZ2VvbV9wb2ludChzaXplPTMpKwogc2NhbGVfY29sb3JfZGlzY3JldGUoYnJlYWtzPWMoIk5pbnRlbmRvIiwiRWxlY3Ryb25pYyBBcnRzIiwiQWN0aXZpc2lvbiIpKSArCiBsYWJzKHRpdGxlPSIgICAgIFRvcCAzIHBsYXRmb3JtcyBmb3IgdGhlIHRvcCAzIHB1Ymxpc2hlcnMiLGNhcHRpb249IlNhbGVzIGluIE1pbGxpb24iKSArCiBnZW9tX3NlZ21lbnQoYWVzKHg9UGxhdGZvcm0seGVuZD1QbGF0Zm9ybSwgeT0wLCB5ZW5kPUdMb2JhbF9TYWxlcykpKwogZ2VvbV90ZXh0KGFlcyhsYWJlbD1HTG9iYWxfU2FsZXMpLCBoanVzdCA9IC0wLjMsIHNpemUgPSAyLjYsZm9udGZhY2UgPSAiYm9sZCIsY29sb3I9J2JsYWNrJykgKwogdGhlbWUoIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNyxoanVzdD0tMC40KSkgKwogICBmYWNldF93cmFwKH4gUHVibGlzaGVyX2YsIG5yb3cgPSA1LCBzY2FsZXMgPSAnZnJlZScsIHN0cmlwLnBvc2l0aW9uID0gJ3JpZ2h0JykrCiB5bGltKDAsIG1heCh0b3BfbmV3JEdMb2JhbF9TYWxlcyArIDEwKSkreWxhYigiR2xvYmFsIFNhbGVzIikrCiBjb29yZF9mbGlwKCkKYGBgCgojIyMjIyMgMS5kICBCZXN0IHNlbGxpbmcgZ2VucmUgZm9yIHRoZSBUT1AgMyBwdWJsaXNoZXJzCiogVG9wIDMgZ2VucmVzIGZvciBlYWNoIHB1Ymxpc2hlcjoKKiBOaW50ZW5kbzogU3BvcnRzLCBSb2xlLXBsYXlpbmcsIFBsYXRmb3JtCiogRUE6IHNwb3J0cywgc2hvb3RlciwgcmFjaW5nCiogQWN0aXZpc2lvbjogc3BvcnRzLCBzaG9vdGVyLCBhY3Rpb24KCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz1GQUxTRX0KdG9wX3RocmVlX3B1Ymxpc2hlciA8LSBzdWJzZXQoZ2FtZXMsUHVibGlzaGVyICVpbiUgYygiTmludGVuZG8iLCJFbGVjdHJvbmljIEFydHMiLCJBY3RpdmlzaW9uIikpCnRvcF90aHJlZV9wdWJsaXNoZXIkUG9ydGFibGUgPC0gZmFjdG9yKHRvcF90aHJlZV9wdWJsaXNoZXIkUG9ydGFibGUpCnRvcF90aHJlZV9wdWJsaXNoZXJfcGxhdGZvcm0gPC0gdG9wX3RocmVlX3B1Ymxpc2hlciAlPiUKc2VsZWN0KFB1Ymxpc2hlcixHZW5yZSxQb3J0YWJsZSxHTG9iYWxfU2FsZXMpJT4lCmdyb3VwX2J5KFB1Ymxpc2hlcixHZW5yZSxQb3J0YWJsZSklPiUKc3VtbWFyaXNlKEdMb2JhbF9TYWxlcz1zdW0oR0xvYmFsX1NhbGVzKSklPiUKYXJyYW5nZShkZXNjKEdMb2JhbF9TYWxlcykpCnRvcF9uZXcgPC0gdG9wX3RocmVlX3B1Ymxpc2hlcl9wbGF0Zm9ybSU+JWdyb3VwX2J5KFB1Ymxpc2hlciklPiV0b3BfbigzKQp0b3BfbmV3JFB1Ymxpc2hlcl9nID0gZmFjdG9yKHRvcF9uZXckUHVibGlzaGVyLCBsZXZlbHM9YygiTmludGVuZG8iLCJFbGVjdHJvbmljIEFydHMiLCJBY3RpdmlzaW9uIikpCmdncGxvdCh0b3BfbmV3LCBhZXMoeD1HZW5yZSwgeT1HTG9iYWxfU2FsZXMsIGNvbG9yPVB1Ymxpc2hlcixzaGFwZT1Qb3J0YWJsZSkpICsKICBnZW9tX3BvaW50KHNpemU9MykrCnNjYWxlX2NvbG9yX2Rpc2NyZXRlKGJyZWFrcz1jKCJOaW50ZW5kbyIsIkVsZWN0cm9uaWMgQXJ0cyIsIkFjdGl2aXNpb24iKSkrCmxhYnModGl0bGU9IlRoZSB0b3AgMyBnZW5yZSBmb3IgdGhlIHRvcCAzIHB1Ymxpc2hlcnMiLGNhcHRpb249IlNhbGVzIGluIE1pbGxpb24iKSArCmdlb21fc2VnbWVudChhZXMoeD1HZW5yZSx4ZW5kPUdlbnJlLCB5PTAsIHllbmQ9R0xvYmFsX1NhbGVzKSkrCmdlb21fdGV4dChhZXMobGFiZWw9R0xvYmFsX1NhbGVzKSwgaGp1c3QgPSAtMC4zLCBzaXplID0gMi42LGZvbnRmYWNlID0gImJvbGQiLGNvbG9yPSdibGFjaycpICsKdGhlbWUoIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNyxoanVzdD0tMC41KSkgKwogIGZhY2V0X3dyYXAofiBQdWJsaXNoZXJfZywgbnJvdyA9IDUsIHNjYWxlcyA9ICdmcmVlJywgc3RyaXAucG9zaXRpb24gPSAncmlnaHQnKSsKeWxpbSgwLCBtYXgodG9wX25ldyRHTG9iYWxfU2FsZXMgKyAxMCkpK3lsYWIoIkdsb2JhbCBTYWxlcyIpKwpjb29yZF9mbGlwKCkKYGBgCgojIyMjIyMgMS5lICBHZW5yZSBmcmVxdWVuY3kgb3ZlciB0aGUgeWVhcnMgYW5kIFBsYXRmb3JtIHBvcHVsYXJpdHkgb3ZlciB0aGUgeWVhcnMKKiBIZXJlLCB0aGUgcG9wdWxhcml0eSBpcyBkZWZpbmVkIGFzIHRoZSBudW1iZXIgb2YgZ2FtZXMgcHVibGlzaGVkIG9uIGVhY2ggcGxhdGZvcm0uIEluIHRoZSBlYXJseSAxOTgwcywgdGhlcmUgd2VyZSBsaW1pdGVkIGFtb3VudCBvZiBwbGF0Zm9ybXMgaW4gdGhlIG1hcmtldCAoMjYwMCwgTkVTKSwgYnV0IGxhdGVyIG9uLCBtb3JlIGFuZCBtb3JlIHBsYXRmb3JtcyBlbWVyZ2VkLiBJdCBpcyBhbHNvIGludGVyZXN0aW5nIHRvIHNlZSB0aGF0IGRpZmZlcmVudCBwbGF0Zm9ybSB0ZW5kcyB0byBkb21pbmF0ZSB0aGUgbWFya2V0IGluIGRpZmZlcmVudCBwZXJpb2RzLCBhbmQgdGhlcmUgaXMgYSB0cmVuZCB0byBzaGlmdCBmcm9tIGxlc3MgcG9ydGFibGUgdG8gcG9ydGFibGUgb25lcy4gCgp8IFllYXJzICAgICAgICAgIHwgICAgICBNb3N0IFBvcHVsYXIgUGxhdGZvcm0gIHwgTW9zdCBQcm9maXRhYmxlIFBsYXRmb3JtIHwgICAKfC0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLTp8IDotLS0tLS0tLS0tOnwgICAKfCAxOTgwIC0gMTk4MiB8ICAyNjAwICB8ICAyNjAwIHwKfDE5ODN8IDI2MDAgfCBORVN8CnwgMTk4NCAtIDE5ODggfCAgTkVTICAgfCAgTkVTICB8CnwgMTk4OSAgICAgICAgfCAgR0IgICAgfCAgR0IgICB8CnwgMTk5MCAgICAgICAgfCAgTkVTICAgfCAgU05FUyB8CnwgMTk5MSAtIDE5OTQgfCAgU05FUyAgfCAgU05FUyB8CnwgMTk5NSAtIDIwMDAgfCAgUFMgICAgfCAgUFMgICB8IAp8IDIwMDEgLSAyMDA1IHwgIFBTMiAgIHwgIFBTMiAgfAp8IDIwMDYgICAgICAgIHwgIFBTMiAgIHwgIFdpbGwgfAp8IDIwMDcgLSAyMDEwIHwgIERTICAgIHwgIFdpbGwgfCAKfCAyMDExIC0gMjAxNCB8ICBQUzMgICB8ICBQUzMgIHwKfCAyMDE1IC0gMjAxNiB8ICBQUzQgICB8ICBQUzQgIHwKCmBgYHtyfQpnYW1lcyAlPiUgIGdyb3VwX2J5KFllYXIsIEdlbnJlKSAlPiUKIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lIGFycmFuZ2UoWWVhciwgZGVzYyhDb3VudCkpICU+JQogc3ByZWFkKGtleSA9IEdlbnJlLCB2YWx1ZSA9IENvdW50LCBmaWxsID0gMCkgJT4lCiBnYXRoZXIoMjoxMywga2V5ID0gIkdlbnJlIiwgdmFsdWUgPSAiQ291bnQiICkgJT4lCiBhcnJhbmdlKFllYXIsIGRlc2MoQ291bnQpKSAtPiBHZW5yZVBvcAogZ2dwbG90KEdlbnJlUG9wLCBhZXMoeCA9IEdlbnJlLCB5ID0gQ291bnQpKSArCiAgICAgICAgICBnZW9tX3BvaW50KGFlcyhmcmFtZSA9IFllYXIsIGNvbG9yID0gR2VucmUsIHNpemUgPSBDb3VudCkpICsgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTY1LCB2anVzdD0wLjUpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKwogc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLG1heChHZW5yZVBvcCRDb3VudCksNTApKSsKICAgZ2d0aXRsZSgiR2VucmUgZnJlcXVlbmN5IG92ZXIgdGhlIHllYXJzIiktPiBwcG4zCmdncGxvdGx5KHBwbjMpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgd2FybmluZz1GQUxTRX0KZ2FtZXMgJT4lICBncm91cF9ieShZZWFyLCBQbGF0Zm9ybSkgJT4lCiBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSBhcnJhbmdlKFllYXIsIGRlc2MoY291bnQpKSAlPiUKIHNwcmVhZChrZXkgPSBQbGF0Zm9ybSwgdmFsdWUgPSBjb3VudCwgZmlsbCA9IDApICU+JQogZ2F0aGVyKDI6MzIsIGtleSA9ICJQbGF0Zm9ybSIsIHZhbHVlID0gImNvdW50IiApICU+JQogYXJyYW5nZShZZWFyLCBkZXNjKGNvdW50KSkgLT4gUGxhdGZvcm1Qb3AKIGdncGxvdChQbGF0Zm9ybVBvcCwgYWVzKHggPSBQbGF0Zm9ybSwgeSA9IGNvdW50KSkgKwogICAgICAgICAgZ2VvbV9wb2ludChhZXMoZnJhbWUgPSBZZWFyLCBjb2xvciA9IFBsYXRmb3JtLCBzaXplID0gY291bnQpKSArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT02NSwgdmp1c3Q9MC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCxtYXgoUGxhdGZvcm1Qb3AkY291bnQpLDUwKSkrCiAgZ2d0aXRsZSgiUGxhdGZvcm0gcG9wdWxhcml0eSBvdmVyIHRoZSB5ZWFycyIpIC0+IHBwbjIKZ2dwbG90bHkocHBuMikKYGBgCgoKIyMjIDIuIFJlZ2lvbmFsIHRyZW5kcyBhbmQgYW5hbHlzaXMgCiMjIyMjIyAyLmEgVG9wIDMgYmVzdCBzZWxsaW5nIHBsYXRmb3JtIGJ5IHJlZ2lvbnMKKiBXZSBzdHVkaWVkIHRoZSByZWdpb25hbCB0cmVuZHMgYnkgbG9va2luZyBhdCB0aGUgdG9wIHBsYXRmb3JtcyBmb3IgZWFjaCByZWdpb24gYXMgc2hvd24gZnJvbSB0aGUgYmFyIGNoYXJ0LgpgYGB7ciBlY2hvPVR9CmdhbWVzIDwtIGdhbWVzWyEoZ2FtZXMkWWVhciAlaW4lIGMoIk4vQSIsICIyMDE3IiwgIjIwMjAiKSksXQpnYW1lcyA8LSBnYW1lcyAlPiUgZ2F0aGVyKFJlZ2lvbiwgUmV2ZW51ZSwgNzoxMCkgCmdhbWVzJFJlZ2lvbiA8LSBmYWN0b3IoZ2FtZXMkUmVnaW9uKQoKbXl0aGVtZV8xIDwtIGZ1bmN0aW9uKCkgewogIAogcmV0dXJuKHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSAxMCwgdmp1c3QgPSAwLjQpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgdmp1c3QgPSAyKSxheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCB2anVzdCA9IC0wLjM1KSkpCiAgCn0KCm15dGhlbWVfMiA8LSBmdW5jdGlvbigpIHsKICAKIHJldHVybih0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIHZqdXN0ID0gMC40KSwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUsIHZqdXN0ID0gMiksYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgdmp1c3QgPSAtMC4zNSkpKQogIAp9CgpteWNvbG9ycyA8LSBjKCIjNzcxQzE5IiwgIiNBQTM5MjkiLCAiIzhFOUNBMyIsICIjNTU2NjcwIiwgIiMwMDAwMDAiLCAiI0UyNTAzMyIsICIjRjI3MzE0IiwgIiNGOEEzMUIiLCAiI0UyQzU5RiIsICIjQjZDNUNDIiwiIzc3MUMxOSIsICIjQUEzOTI5IiwgIiM4RTlDQTMiLCAiIzU1NjY3MCIsICIjMDAwMDAwIiwgIiNFMjUwMzMiLCAiI0YyNzMxNCIsICIjRjhBMzFCIiwgIiNFMkM1OUYiLCAiI0I2QzVDQyIsIiM3NzFDMTkiLCAiI0FBMzkyOSIsICIjOEU5Q0EzIiwgIiM1NTY2NzAiLCAiIzAwMDAwMCIsICIjRTI1MDMzIiwgIiNGMjczMTQiLCAiI0Y4QTMxQiIsICIjRTJDNTlGIiwgIiNCNkM1Q0MiLCIjOEU5Q0EzIikKCgp0b3BfcGxhdGZvcm1fcmVnaW9uIDwtIGdhbWVzICU+JQogICAgICAgICAgICAgZ3JvdXBfYnkoUmVnaW9uLCBQbGF0Zm9ybSkgJT4lCiAgICAgICAgICAgICBzdW1tYXJpemUoUmV2ZW51ZSA9IHN1bShSZXZlbnVlKSkgJT4lCiAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoUmV2ZW51ZSkpICU+JQogICAgICAgICAgICAgdG9wX24oMykKCnRlZCA8LSBnZ3Bsb3QodG9wX3BsYXRmb3JtX3JlZ2lvbiwgYWVzKFJlZ2lvbiwgUmV2ZW51ZSwgZmlsbCA9IFBsYXRmb3JtKSkgKyAKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiKSAgKwogIGdndGl0bGUoIlRvcCAzIGJlc3Qgc2VsbGluZyBwbGF0Zm9ybSBieSByZWdpb25zIikgKwogIHlsYWIoIlJldmVudWUgaW4gTWlsbGlvbnMiKSArCiAgeGxhYigiUmVnaW9uIikgKwogIG15dGhlbWVfMigpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjOEU5Q0EzIiwiI0Y4QTMxQiIsICIjQUEzOTI5IiwgIiNFMjUwMzMiLCAiI0UyQzU5RiIsICIjNTU2NjcwIikpCmdncGxvdGx5KHRlZCkKYGBgCgojIyMjIyMgMi5iIFRvcCAzIGJlc3Qgc2VsbGluZyBwdWJsaXNoZXIgYnkgcmVnaW9ucwoqIFdlIHN0dWRpZWQgdGhlIHJlZ2lvbmFsIHRyZW5kcyBieSBsb29raW5nIGF0IHRoZSB0b3AgMyBiZXN0IHNlbGxpbmcgcHVibGlzaGVycyBmb3IgZWFjaCByZWdpb24uCmBgYHtyIGVjaG89VFJVRSxyZXN1bHRzPUYsZmlnLndpZHRoPTEwfQp0b3BfZ2VucmVzX3JlZ2lvbiA8LSBnYW1lcyAlPiUKICAgICAgICAgICAgIGdyb3VwX2J5KFJlZ2lvbiwgUHVibGlzaGVyKSAlPiUKICAgICAgICAgICAgIHN1bW1hcml6ZShSZXZlbnVlID0gc3VtKFJldmVudWUpKSAlPiUKICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhSZXZlbnVlKSkgJT4lCiAgICAgICAgICAgICB0b3BfbigzKQoKdGVkMiA8LSBnZ3Bsb3QodG9wX2dlbnJlc19yZWdpb24sIGFlcyhSZWdpb24sIFJldmVudWUsIGZpbGwgPSBQdWJsaXNoZXIpKSArIAogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICArCiAgZ2d0aXRsZSgiVG9wIDMgYmVzdCBzZWxsaW5nIHB1Ymxpc2hlciBieSByZWdpb24iKSArCiAgeWxhYigiU2FsZXMgaW4gTWlsbGlvbnMiKSArCiAgeGxhYigiUmVnaW9uIikgKwogIG15dGhlbWVfMigpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKCmdncGxvdGx5KHRlZDIpCmBgYAoKIyMjIyMjIDIuYyBCZXN0IHNlbGxpbmcgZ2VucmUgZm9yIHBhcnRpY3VsYXIgcmVnaW9ucyAKKlRoaXMgaGVhdCBtYXAgaWRlbnRmaWVzIHRoZSB0b3Agc2VsbGluZyBnZW5yZXMgZm9yIGVhY2ggcmVnaW9uIGJ5IGRpc3BsYXlpbmcgYSBkZWVwZXIgY29sb3IgKHB1cnBsZSkgZm9yIGdlbnJlcyB3aXRoIGhpZ2ggcmV2ZW51ZXMuCmBgYHtyIGVjaG89VFJVRSxmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01LHJlc3VsdHM9Rn0KeWVhcl9nZW5yZSA8LSBnYW1lcyAlPiUgCiAgICAgICAgICAgICAgICBncm91cF9ieShZZWFyLCBHZW5yZSwgUmVnaW9uKSAlPiUgCiAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShUb3RhbFJldmVudWUgPSBzdW0oUmV2ZW51ZSkpIAogICAgICAgICAgICAgICAgCgpnZ3Bsb3QoeWVhcl9nZW5yZSwgYWVzKFllYXIsIEdlbnJlLCBmaWxsID0gVG90YWxSZXZlbnVlKSkgKwogICAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikgKwogICAgZ2d0aXRsZSgiICAgICAgICAgICAgICAgICAgICAgICBCZXN0IHNlbGxpbmcgZ2VucmUgZm9yIHBhcnRpY3VsYXIgcmVnaW9ucyIpICsgCiAgICBmYWNldF93cmFwKHZhcnMoUmVnaW9uKSwgbmNvbCA9IDQpICsKdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkrCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJwaW5rIiwgaGlnaD0gInB1cnBsZSIpKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InBpbmsiLCBoaWdoPSAicHVycGxlIikKYGBgCgojIyMjIyMgMi5kIFRvcCAzIGJlc3Qgc2VsbGluZyBnZW5yZSBieSByZWdpb24KKiBXZSBzdHVkaWVkIHRoZSByZWdpb25hbCB0cmVuZHMgYnkgbG9va2luZyBhdCB0aGUgdG9wIDMgYmVzdCBzZWxsaW5nIGdlbnJlcyBmb3IgZWFjaCByZWdpb24uCmBgYHtyIGVjaG89VFJVRSxyZXN1bHRzPUYsZmlnLndpZHRoPTh9CnRvcF9nZW5yZXNfcmVnaW9uIDwtIGdhbWVzICU+JQogICAgICAgICAgICAgZ3JvdXBfYnkoUmVnaW9uLCBHZW5yZSkgJT4lCiAgICAgICAgICAgICBzdW1tYXJpemUoUmV2ZW51ZSA9IHN1bShSZXZlbnVlKSkgJT4lCiAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoUmV2ZW51ZSkpICU+JQogICAgICAgICAgICAgdG9wX24oMykKCnRlZDIgPC0gZ2dwbG90KHRvcF9nZW5yZXNfcmVnaW9uLCBhZXMoUmVnaW9uLCBSZXZlbnVlLCBmaWxsID0gR2VucmUpKSArIAogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICArCiAgZ2d0aXRsZSgiVG9wIDMgYmVzdCBzZWxsaW5nIGdlbnJlIGJ5IHJlZ2lvbiIpICsKICB5bGFiKCJSZXZlbnVlIGluIE1pbGxpb25zIikgKwogIHhsYWIoIlJlZ2lvbiIpICsKICBteXRoZW1lXzIoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCgpnZ3Bsb3RseSh0ZWQyKQpgYGAKCgojIyMgMyBJbnZlc3RtZW50IG9wdGlvbnMgYW5kIHJlY29tbWVuZGF0aW9ucyAKIyMjIyMjIDMuYSBHbG9iYWwgc2FsZXMgcHJvcG9ydGlvbiBieSByZWdpb24gCiogRm9yIHRoZSBtb3N0IHBhcnQgd2Ugc2VlIHRoYXQgdGhlIE5vcnRoIEFtZXJpY2EgYWNjb3VudHMgZm9yIHRoZSBoaWdoZXN0IHByb3BvcnRpb24gb2YgZ2xvYmFsIHNhbGVzLiBXZSBjYW4gYWxzbyBzZWUgdGhhdCBFdXJvcGVhbiBzYWxlcyBhcmUgb24gYW4gaW5jbGluZSBhbmQgYWN0dWFsbHkgc3VycGFzcyBOb3J0aCBhbWVyaWNhbiBzYWxlcyBieSB0aGUgeWVhciAyMDE1LTIwMTYuIEV2ZW4gdGhvdWdoIEphcGFuJ3MgcHJvcG9ydGlvbiBvZiBHbG9iYWwgc2FsZXMgc2VlbSB0byBiZSBkZWNsaW5pbmcgaW4gdGhlIHBhc3QgeWVhcnMsIGZvciB0aGUgbW9zdCByZWNlbnQgeWVhcnMgaXQgc2VlbXMgdG8gYmUgc3RlYWRpbHkgaW5jbGluaW5nLiAKYGBge3IgZWNobz1ULHJlc3VsdHM9Rn0KZ2FtZXMgPC0gcmVhZF9jc3YoImdhbWVzLmNzdiIpIApkZl90cmlhbCA8LSBkYXRhX2ZyYW1lKHNvcnQoZ2FtZXMkWWVhciksIE5BX1NhbGVzID0gZ2FtZXMkTkFfU2FsZXMsIGdhbWVzJEVVX1NhbGVzLAogICAgICAgICAgICAgICAgICAgICAgZ2FtZXMkSlBfU2FsZXMsIGdhbWVzJE90aGVyX1NhbGVzLCBnYW1lcyRHTG9iYWxfU2FsZXMpCmdhbWVzMyA8LSBnYW1lcyAlPiUKIHNlbGVjdChZZWFyLCBOQV9TYWxlcywgRVVfU2FsZXMsIEpQX1NhbGVzLCBPdGhlcl9TYWxlcyxHTG9iYWxfU2FsZXMpICU+JQogZ3JvdXBfYnkoWWVhcikgJT4lCiBzdW1tYXJpc2UoTkFfc2FsZXNfcHJvcCA9IHN1bShOQV9TYWxlcykvc3VtKEdMb2JhbF9TYWxlcyksCiAgICAgICAgICAgRVVfc2FsZXNfcHJvcCA9IHN1bShFVV9TYWxlcykvc3VtKEdMb2JhbF9TYWxlcyksCiAgICAgICAgICAgSlBfc2FsZXNfcHJvcCA9IHN1bShKUF9TYWxlcykvc3VtKEdMb2JhbF9TYWxlcyksCiAgICAgICAgICAgT3RoZXJfc2FsZXNfcHJvcCA9IHN1bShPdGhlcl9TYWxlcykvc3VtKEdMb2JhbF9TYWxlcyksCiAgICAgICAgICAgR2xvYmFsX3NhbGVzX3Byb3AgPSBzdW0oR0xvYmFsX1NhbGVzKS9zdW0oR0xvYmFsX1NhbGVzKSkKbXljb2xvcnMgPC0gYygiIzc3MUMxOSIsICIjQUEzOTI5IiwgIiM4RTlDQTMiLCAiIzU1NjY3MCIsICIjMDAwMDAwIiwgIiNFMjUwMzMiLCAiI0YyNzMxNCIsICIjRjhBMzFCIiwgIiNFMkM1OUYiLCAiI0I2QzVDQyIpCnJlZ2lvbnMgPC0gYyggImRhcmtncmVlbiIgPSAiTm9ydGggQW1lcmljYSIsICJibHVlIiA9ICJFdXJvcGUiICwgICJzaWVubmEiID0gIkphcGFuIiAsICJvcmFuZ2UiID0gIk90aGVyIFJlZ2lvbnMiLCAiYmxhY2siID0gIkdsb2JhbCIpCgpnZ3Bsb3QoZ2FtZXMzLGFlcyh4PVllYXIpKSsKICBnZW9tX2xpbmUoYWVzKHkgPSBOQV9zYWxlc19wcm9wICxjb2xvciA9ICIjNzcxQzE5IikpKwogIGdlb21fbGluZShhZXMoeSA9IEVVX3NhbGVzX3Byb3AgLCBjb2xvciA9ICIjQjZDNUNDIikpKwogIGdlb21fbGluZShhZXMoeSA9IEpQX3NhbGVzX3Byb3AsIGNvbG9yID0gIiNFMjUwMzMiICkpKwogIGdlb21fbGluZShhZXMoeSA9IE90aGVyX3NhbGVzX3Byb3AsIGNvbG9yID0gIiNFMkM1OUYiKSkrCiAgZ2VvbV9saW5lKGFlcyh5ID0gR2xvYmFsX3NhbGVzX3Byb3AsIGNvbG9yID0gImJsYWNrIikpKwogIGxhYnModGl0bGUgPSAiU2FsZXMgcGVyIHJlZ2lvbiBmcm9tIDE5ODAtMjAxNiIsIHkgPSAiUGVyY2VudGFnZSBvZiBnbG9iYWwgc2FsZXMiKSsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJSZWdpb25zIiwgdmFsdWVzID0gYyggImRhcmtncmVlbiIsICJibHVlIiwgInNpZW5uYSIgLCAib3JhbmdlIiwgImJsYWNrIiksbGFiZWxzPWMoIk5vcnRoIEFtZXJpY2EiLCAiRXVyb3BlIiAsICJKYXBhbiIgLCAiT3RoZXIgUmVnaW9ucyIsICJHbG9iYWwiKSkKYGBgCgojIyMjIyMgMy5iIFN1bW1hcnkgCgp8IE9wdGlvbiB8ICAgICBOb3J0aCBBbWVyaWNhICB8IEV1cm9wZSB8IEphcGFuIHwKfC0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLTp8IDotLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLXwKfFB1Ymxpc2hlcnwgTmludGVuZG8gfCBOaW50ZW5kbyB8TmludGVuZG8gfAp8UGxhdGZvcm0gfCBYMzYwIHwgUFMzIHwgRFMgfAp8R2VucmUgICB8IEFjdGlvbiB8IEFjdGlvbiB8IFJvbGUtUGxheWluZ3wKCgoKIyMjIEFwcGVuZGl4CgojIyMjIyMgRGF0YSBFeHBsb3JhdGlvbi4gCmBgYHtyIGVjaG89VFJVRSxmaWcuaGVpZ2h0PTh9CmdncGxvdChnYW1lcywgYWVzKHg9WWVhciwgZmlsbD0uLmNvdW50Li4pKSArCiAgICBnZW9tX2JhcigpKwogICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSIjNzcxQzE5IiwgaGlnaD0gIiNGMjczMTQiKSsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93PSIjNzcxQzE5IiwgaGlnaD0gIiNGMjczMTQiKSsKICAgIGxhYnModGl0bGU9Ik51bWJlciBvZiBHYW1lcyBSZWxlYXNlZCBldmVyeSBZZWFyIiwgeD0gIlllYXIiLCAKICAgICAgICAgeT0gIlRvdGFsIE51bWJlciBvZiBHYW1lcyIpKwogICAgZ2VvbV90ZXh0KHN0YXQ9J2NvdW50JyxhZXMobGFiZWw9Li5jb3VudC4uKSwgaGp1c3Q9LTAuMSxjb2xvcj0iYmxhY2siLCBzaXplPTIuNSkrCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTk4MDoyMDE2KSArIHRoZW1lX21pbmltYWwoKSsKICBjb29yZF9mbGlwKCkKYGBgCgojIyMjIyMgRGF0YSBFeHBsb3JhdGlvbiAyIApgYGB7cn0Kb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpClBvcnRhYmxlIDwtIGZ1bmN0aW9uKGRmKSB7CiBsZW4gPC0gbGVuZ3RoKGRmJFBsYXRmb3JtKQogbmV3X3ZlYyA8LSB2ZWN0b3IobW9kZSA9ICJudW1lcmljIiwgbGVuZ3RoID0gbGVuKQogcHJvdHZlYyA8LSBjKCJEUyIsICJHQiIsICIzRFMiLCAiR0JBIikKIGZvciAoaSBpbiAxOmxlbikgIHsKICAgaWYgKGRmJFBsYXRmb3JtW2ldICVpbiUgcHJvdHZlYykgewogICAgIG5ld192ZWNbaV0gPC0gMQogICB9IGVsc2UgewogICAgIG5ld192ZWNbaV0gPC0gMAogICB9CiB9CiByZXR1cm4obmV3X3ZlYykKfQpnYW1lcyRQb3J0YWJsZSA8LSBQb3J0YWJsZShnYW1lcykKCmdhbWVzMjwtIGdhbWVzCgpnYW1lczIgJT4lCiB0cmFuc211dGUoWWVhcjIwMDggPSBjdXQoWWVhciwgYnJlYWtzID0gYygxOTgwLCAyMDA4LCAyMDE3KSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJCZWZvcmUgMjAwOCIsICJBZnRlciAyMDA4IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLCByaWdodCA9IEZBTFNFKSwKICAgICAgICAgICBQb3J0YWJsZSA9IGZhY3RvcihQb3J0YWJsZSksCiAgICAgICAgICAgU2FsZXMgPSBHTG9iYWxfU2FsZXMpICU+JQogZ3JvdXBfYnkoWWVhcjIwMDgsIFBvcnRhYmxlKSAlPiUKIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgLT4gZ2FtZXMzCgpnZ3Bsb3QoZ2FtZXMzKSArCiBnZW9tX2JhcihhZXMoeCA9IFBvcnRhYmxlICwgeSA9IGNvdW50LCBmaWxsID0gWWVhcjIwMDgpLAogICAgICAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikgKwogbGFicyhzdWJ0aXRsZT0iUG9ydGFibGUgVnMuIE5vbi1Qb3J0YWJsZSIsIHg9ICJUeXBlIiwKICAgICAgICB5PSAiVG90YWwgTnVtYmVyIG9mIEdhbWVzIFJlbGVhc2VkIikgKwogdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIApgYGAKCiMjIyMjIyBEYXRhIEV4cGxvcmF0aW9uIDMgCmBgYHtyIGVjaG89VFJVRSxmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01LHJlc3VsdHM9RiwgZmlnLndpZHRoPSAxMH0Kb2sgPC0gZ2FtZXMgJT4lIHNlbGVjdChZZWFyLEdMb2JhbF9TYWxlcyxHZW5yZSklPiVncm91cF9ieShZZWFyLEdlbnJlKSU+JQogIHN1bW1hcmlzZShUb3RhbF9zYWxlcz1zdW0oR0xvYmFsX1NhbGVzKSkgCm9rMSA8LSBhcnJhbmdlKG9rLCBkZXNjKFllYXIpKQoKcGxvdF9seShvazEsIHggPSB+VG90YWxfc2FsZXMsIHkgPSB+R2VucmUsIHogPSB+WWVhcikgJT4lIGxheW91dCggdGl0bGUgPSJTYWxlcyBieSBnZW5yZSBmcm9tIDE5ODAgLSAyMDE2IikgJT4lCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+R2VucmUsIHNpemUgPSAwLjUpCgpgYGAKCgoKCgoKCgo=